﻿using UnityEngine;
using System.Collections;
using System;
using UnityEngine.Events;

///Developed By Indie Studio
///https://assetstore.unity.com/publishers/9268
///www.indiestd.com
///info@indiestd.com
 
namespace IndieStudio.BugsBang.Utility
{
	[DisallowMultipleComponent]
	public class TransformFollow2D: MonoBehaviour
	{
		/// <summary>
		/// Whether follow is running or not.
		/// </summary>
		public bool isRunning = true;

		/// <summary>
		/// The target transform.
		/// </summary>
		public Transform target;

		/// <summary>
		/// The follow speed.
		/// </summary>
		public float speed = 1;

		/// <summary>
		/// The follow method.
		/// </summary>
		public FollowMethod followMethod = FollowMethod.LERP;

		/// <summary>
		/// The follow offset.
		/// </summary>
		public Vector2 followOffset = Vector2.zero;

		/// <summary>
		/// Whether to follow x , follow y coordinates.
		/// </summary>
		public bool followX = true, followY = true;

		/// <summary>
		/// Whether to apply follow sign.
		/// </summary>
		public bool applyFollowSign;

		/// <summary>
		/// Whether the follow is continuous or not.
		/// </summary>
		public bool continuousFollow;

		/// <summary>
		/// Whether reached target or not.
		/// </summary>
		public bool reachedTarget;

		/// <summary>
		/// A temp vector position.
		/// </summary>
		private Vector2 tempPosition;

		/// <summary>
		/// A temp vector.
		/// </summary>
		private Vector3 tempVector;

		/// <summary>
		/// The minimum reach offset.
		/// </summary>
		private float minReachOffset = 0.1f;

		/// <summary>
		/// On reach target unity event.
		/// </summary>
		public UnityEvent onReachTargeEvent;

		void Start ()
		{
			minReachOffset = Math.Abs (minReachOffset);
			reachedTarget = false;
		}

		// Update is called once per frame
		void Update ()
		{
			if (!isRunning || target == null) {
				return;
			}

			if (reachedTarget && !continuousFollow) {
				return;
			} 

			//initial postion
			tempVector = transform.position;

			if (followX) {
				tempVector.x = GetValue (transform.position.x, target.position.x + (Mathf.Sign (transform.localScale.x) * followOffset.x));
			} else {
				tempVector.x = transform.position.x;
			}

			if (followY) {
				tempVector.y = GetValue (transform.position.y, target.position.y + followOffset.y);
			} else {
				tempVector.y = transform.position.y;
			}

			//apply new postion
			transform.position = tempVector;

			if (applyFollowSign) {
				//initial scale
				tempVector = transform.localScale;

				if (transform.position.x < target.transform.position.x + (Mathf.Sign (transform.localScale.x) * followOffset.x)) {
					tempVector.x = Mathf.Abs (tempVector.x);
				} else {
					tempVector.x = -Mathf.Abs (tempVector.x);
				}
				transform.localScale = tempVector;
			}

			if (NearByTarget ()) {
				reachedTarget = true;
				onReachTargeEvent.Invoke ();
			}
		}

		/// <summary>
		/// Whether the object is nearby the target or not.
		/// </summary>
		/// <returns><c>true</c>, if the target is nearby, <c>false</c> otherwise.</returns>
		private bool NearByTarget ()
		{
			tempPosition = transform.position;

			if (followX && !followY) {
				tempPosition.y = target.position.y;
			} else if (followY && !followX) {
				tempPosition.x = target.position.x;
			} 

			return Vector2.Distance (target.position, tempPosition) <= TotalOffset();
		}

		/// <summary>
		/// The total reach offset.
		/// </summary>
		/// <returns>The offset.</returns>
		private float TotalOffset(){
			return minReachOffset + followOffset.magnitude;
		}

		/// <summary>
		/// Gets the follow value.
		/// </summary>
		/// <returns>The value.</returns>
		/// <param name="currentValue">Current value.</param>
		/// <param name="targetValue">Next Target value.</param>
		private float GetValue (float currentValue, float targetValue)
		{
			float returnValue = 0;

			if (followMethod == FollowMethod.LERP) {
				returnValue = Mathf.Lerp (currentValue, targetValue, speed * Time.smoothDeltaTime);
			} else if (followMethod == FollowMethod.MOVE_TOWARDS) {
				returnValue = Mathf.MoveTowards (currentValue, targetValue, speed * Time.smoothDeltaTime);
			} else if (followMethod == FollowMethod.SMOOTH_STEP) {
				returnValue = Mathf.SmoothStep (currentValue, targetValue, speed * Time.smoothDeltaTime);
			}
			return returnValue;
		}

		/// <summary>
		/// On reach the target unity event.
		/// </summary>
		public void OnReachTarget ()
		{
			Debug.Log (transform.name+" reached target at "+Time.time);
		}

		/// <summary>
		/// Move this instance.
		/// </summary>
		public void Move ()
		{
			isRunning = true;
		}

		/// <summary>
		/// Reset this instance.
		/// </summary>
		public void Reset ()
		{
			target = null;
			isRunning = false;
			reachedTarget = false;
		}

		public enum FollowMethod
		{
			LERP,
			SMOOTH_STEP,
			MOVE_TOWARDS
		};
	}
}